/*
 * =====================================================================================
 *
 *       Filename:  buffer.c
 *
 *    Description:  implement the buffer functions 
 *
 *        Version:  1.0
 *        Created:  2011年12月08日 11时11分00秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Regan (), lcqhigh@gmail.com
 *
 * =====================================================================================
 */

#include	<stdlib.h>
#include	<string.h>
#include    <stdio.h> 
#include    <assert.h>

#include    <sys/types.h>
#include	<regex.h>

#include    "buffer.h"
#include	"../common/errproc.h"

#define	ALIGNMENG_SIZE 4			/*  */
#define	BUFFER_BASE_SIZE 512 /*  */
#define	MAX_BUFFER_SIZE  1024 * 1024	/*  */

#define data_align(x) (((x) + ALIGNMENG_SIZE - 1) & ~(ALIGNMENG_SIZE-1))


/* ============================================================================== */
static inline int buffer_add(buffer_t *buf, const void *data, int datlen);
/* ============================================================================== */

static inline int get_write_space(buffer_t *buf)
{
    return buf->buflen - (buf->readptr+buf->datalen); 
}

static inline int get_free_space(buffer_t *buf)
{
    if (buf) {
        return buf->buflen - buf->datalen; 
    }
    return 0;
}

#define	MAX_REALIGN_SIZE 2048			/*  */
#define REALIGN_WATERMARK 512

static inline int should_realign(buffer_t *buf)
{
    if ((buf->readptr > buf->datalen/2) 
         || (buf->readptr > REALIGN_WATERMARK))  // && (buf->readptr <= MAX_REALIGN_SIZE)))
        return 1;
    return 0;
}

size_t readable_bytes(buffer_t *buf)
{
    return buf->datalen; 
}

int buffer_realign(buffer_t *buf)
{
    if (buf) {
         if (buf->datalen > 0)
             memmove(buf->data, buf->data+buf->readptr, buf->datalen);
         buf->readptr = 0;
    }
    return 0;
}

buffer_t *buffer_new(int buflen)
{
    int alloc_size = data_align(buflen);
    assert(alloc_size > 0);
    while (alloc_size < BUFFER_BASE_SIZE)
        alloc_size <<= 1;

    buffer_t *buf = calloc(1, sizeof(buffer_t));
    if (buf) {
        buf->data = calloc(1, alloc_size); 
        if (buf->data == NULL) {
            free(buf);
            return NULL;
        }
        buf->buflen = alloc_size;
        buf->readptr = buf->datalen = buf->peekptr = 0;
    }
    return buf;
}

/* uint16_t buflen; uint16_t readptr; uint16_t datalen; char data[0];*/
void buffer_free(buffer_t *buf)
{
    if (buf) {
        if (buf->data)
            free(buf->data);
        free(buf);
    }
    return;
}

int  buffer_expand(buffer_t *buf, int datlen)
{
    int size_need = buf->datalen + datlen;
    if (buf->buflen >= buf->readptr+size_need)
        ;
    else if (buf->buflen >= size_need && should_realign(buf)) {
        buffer_realign(buf);
    }
    else {
        int alloc_size = data_align(buf->readptr+size_need+4);
        if (alloc_size < 2 * buf->buflen)
            alloc_size = buf->buflen * 2;
        buf->data = realloc(buf->data, alloc_size);
        assert(buf != NULL && buf->data != NULL);

        buf->buflen = alloc_size;
        if (should_realign(buf) == 1) 
            buffer_realign(buf);
    }

    return get_write_space(buf);
}

int buffer_write(buffer_t *buf, const void *data, int datlen)
{
    return buffer_add(buf, data, datlen);
}

int buffer_read(buffer_t *buf, void *data, int datlen)
{
    if (datlen > buf->datalen)
        datlen = buf->datalen;
    
    memcpy(data, buf->data+buf->readptr, datlen);

    buf->readptr += datlen;
    buf->datalen -= datlen;

    if (should_realign(buf) == 1)
        buffer_realign(buf);

    return datlen;
}

int buffer_peek(buffer_t *buf, void *data, int datlen)
{
    if (data == NULL || buf == NULL)
        return 0;

    int avail_data = buf->datalen - buf->peekptr;
    if (datlen > avail_data) 
        datlen = avail_data;
    assert(datlen >= 0);

    char *ptr = buf->data + buf->readptr + buf->peekptr; 
    if (datlen > 0)
        memcpy(data, ptr, datlen);

    buf->peekptr += datlen;
    return datlen;
}

int buffer_seek(buffer_t *buf, int offset, int whence)
{
    int base = 0;
    if (whence == SEEK_CUR)
        base = buf->peekptr;
    else if (whence == SEEK_END)
        base = buf->datalen;

    offset += base;
    if (offset > buf->datalen)
        offset = buf->datalen;
    else if (offset < 0)
        offset = 0;

    buf->peekptr = offset;

    return buf->peekptr;
}

int buffer_rewind(buffer_t *buf)
{
    buf->peekptr = 0;
    return 0;
}


void buffer_clear(buffer_t *buf)
{
    //return buffer_drain(buf, buf->readptr, buf->datalen);
    buf->readptr = buf->datalen = buf->peekptr = 0;
}

static int buffer_add(buffer_t *buf, const void *data, int datlen)
{
    int space = get_write_space(buf);
    if (space < datlen)
        space = buffer_expand(buf, datlen);
    assert(space >= datlen);

    //bug 复制目的指针错误
    char *write_ptr = buf->data + buf->readptr + buf->datalen;
    memcpy(write_ptr, data, datlen);
    buf->datalen += datlen;
    return datlen;
}

int buffer_drain(buffer_t *buf, int start, int len)
{
    int end = start;
    if (len < 0)    {start += len;}
    else            {end += len;}

    if (end < 0 || start > buf->datalen)
        return 0;
    if (start < 0) start = 0;
    if (end > buf->datalen) end = buf->datalen;

    len = end-start;
    int remain = buf->datalen - end;
    char *ptr = buf->data + buf->readptr;
    if (remain > 0 && start > 0)
        memmove(ptr+start, ptr+end, remain);
    else if (remain > 0)
        buf->readptr += len;

    buf->datalen -= len;

    return len;
}

int buffer_movebuf(buffer_t *dst, buffer_t *src)
{
    if (src == NULL || dst == NULL)
        return 0;

    int res = 0;
    if ((-1) == buffer_add(dst, src->data+src->readptr, src->datalen))
        return -1;
    else {
        res = src->datalen;
        src->readptr = src->datalen = 0;
    }

    return  res;
}

//return find or not
int buffer_search(buffer_t *buf, const char *what, int len, int start)
{
    if (buf == NULL || what == NULL)
        return 0;
    
    char *search_ptr = buf->data + buf->readptr + start;
    void *ptr = memmem(search_ptr, buf->datalen-start, what, len); 

    return  (ptr != NULL);
}

//true or false
int buffer_search_all(buffer_t *buf, const char *what, int len)
{
    return buffer_search(buf, what, len, 0);
}

int buffer_regsearch(buffer_t *buf, const char *regexp, regmatch_t outmatches[], int nmatches)
{
    if (buf == NULL || regexp == NULL) return 0;
    const char *str = buf->data + buf->readptr;

    regex_t reg;
    regmatch_t matches[1];
    int st = 0, indx = 0; 

    static const int flag = REG_EXTENDED | REG_ICASE;
    static char  errbuf[128];
    int res = regcomp(&reg, regexp, flag);
    if (res) {
        regerror(res, &reg, errbuf, sizeof(errbuf));
        printf("compile the [%s] failed for %s\n", regexp, errbuf);
        goto out;
    }
    
    res = regexec(&reg, str, 1, matches, 0);
    while (!res && indx < nmatches) {
        outmatches[indx].rm_so = st + matches[0].rm_so;
        outmatches[indx++].rm_eo = st + matches[0].rm_eo;

        st += (matches[0].rm_so+1);
        res = regexec(&reg, str+st, 1, matches, 0);
    }

out:
    regfree(&reg);
    return indx;
}

int buffer_regmatch(buffer_t *buf, const char *regex, int start, int *off, int *end)
{
    int nmatch = 1, ret = 0;
    if (buf == NULL || regex == NULL) return 0;
    if (start < 0) start = 0;
    if (start > buf->datalen) return 0;

    regex_t reg;
    regmatch_t matches[1];

    int res = regcomp(&reg, regex, REG_EXTENDED|REG_ICASE);
    if (res) {
        static char errbuf[128];
        regerror(res, &reg, errbuf, sizeof(errbuf));
        fprintf( stderr, "compile the regexp %s failed:%s\n", regex, errbuf);
        goto out;
    }

    char *match_ptr = buf->data + buf->readptr + start;
    res = regexec(&reg, match_ptr, nmatch, matches, 0);
    if (res == 0) {
        if (off) *off = matches[0].rm_so;
        if (end) *end = matches[0].rm_eo;
        ret = 1;
    }

out:
    regfree(&reg);
    return ret;
}

static void Print_hex(const unsigned char *dat, size_t len)
{
    int k = 0;
    for (; k < len;)
    {
      fprintf(stderr, "%02x  ", dat[k]);
      ++k;
      if (k % 10 == 0)
          fprintf(stderr, "\n");
    }
}

void buffer_print(buffer_t *buf)
{
//    char *cp = buf->data + buf->readptr + buf->datalen;
//    char c = *cp;
//    *cp = '\0';
//    err_debug(stderr, "%s\n", buf->data+buf->readptr);
//    *cp = c;
    fprintf(stderr, "\n=====================================\n" );
    Print_hex((unsigned char*)buf->data+buf->readptr, buf->datalen);
    fprintf(stderr, "\n======================================\n" );
    return;
}
